﻿#region License
/**	
The MIT License (MIT)

Copyright (c) 2013 Philipp Schillinger, Michael Alekseew, Andreas Braun, Steeven Zeiß

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. 
 **/
#endregion

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
using System.Threading.Tasks;
using System.Text;
using System.Windows.Forms;
using Emgu.CV;
using Emgu.CV.CvEnum;
using Emgu.CV.Structure;
using Emgu.CV.UI;
using System.Threading;
using CLEyeMulticam;

namespace IndoorLocalization.UserInterface {
	/// <summary>
	/// Realises the main window of the application.
	/// It is responsible for coordinating all features of the application
	/// and the people localization progress.
	/// </summary>
	public partial class MainWindow : Form {

		//========== Class Overview =========================
		// This class is really huge because it has to handle all GUI-Input-Events
		// and provide the core of the human application interface.
		//
		// It is divided into four regions:
		//------------------------------------------
		// Attributes:
		// All attributes are declared here, all of them are private.
		//
		// Global:
		// Provides functional support to be called from outside.
		// Most of the methods are mapped to static methods by the class Program.
		//
		// Events:
		// Event-Handling can be found in this region.
		// The events are sorted based on the according elements in the GUI.
		//
		// Methods:
		// All private methods that are used by events are contained here.
		//===================================================

		#region [Attributes]
		//======[Attributes]=========================================
		private bool networkActive = false;		// Indicates whether the network is currently active.

		private Image<Bgr, Byte> realMap;		// Provides an image of the map as it has been loaded.
		private Image<Bgr, Byte> wallMap;		// Provides an image of the map only showing walls in black.
		private Image<Bgr, Byte> coverageMap;	// Provides a coverage image of the map (see class FloorPlan).
		private Image<Bgr, Byte> camImage;		// Provides the currently selected camera image.

		private bool drawCoverage = false;		// Indicates whether the coverage map should be displayed.
		private bool drawWalls = false;			// Indicates whether wallMap or realMap should be displayed.
		private bool drawCams = true;			// Indicates whether cams should be drawn into the map.
		private bool drawRays = false;			// Indicates whether rays should be drawn into the map.
		private bool drawEntities = true;		// Indicates whether entities should be drawn into the map.
        private bool drawAOI = false;
		private bool showID = true;				// Indicates whether the ID of an entity should be displayed.
		private bool showReliability = false;	// Indicates whether the reliability of an entity should be displayed.
		private bool showOrigin = false;		// Indicates whether the sensors responsible for detection of an entity should be displayed.
		private bool showPosition = false;		// Indicates whether the exact coordinates of an entity should be displayed.

		private bool covRestoreWalls = false;	// Saves the current drawWalls state when switching to coverage map.
		private bool covRestoreRays = false;	// Saves the current drawRays state when switching to coverage map.

		private ImageBox mapBox;				// A reference to the ImageBox that is currently used for the map.
		private ImageBox camBox;				// A reference to the ImageBox that is currently used for the cams.
		private List<Panel> camListPanels;		// A list of all panels that are displayed in the Cams-Tab, each panel visualizes one cam.
		private Panel selectedCamPanel;			// Reference to the currently selected panel of all cam panels.
		private SensorCam selectedCam {
			get { return (selectedCamPanel != null) ?(SensorCam)selectedCamPanel.Tag : null; }
		}		// Read-only reference to the currently selected cam depending on the SelectedCamPanel.
		private int addCamSelectedIndex;		// Index of the cam device that is currently selected to be added.

		private Bgr camDefaultColor = new Bgr(Color.Blue);		// Used for drawing cams.
		private Bgr camSelectedColor = new Bgr(Color.Aqua);		// Used for drawing the selected cam.
		private Bgr camPausedColor = new Bgr(Color.Magenta);	// Used for drawing a paused cam.
		private Bgr camAddColor = new Bgr(Color.Gray);			// Used for drawing of the cam that should be added.
		private Bgr rayColor = new Bgr(Color.Orange);			// Used for drawing rays.
		private Bgr entityColor = new Bgr(0, 180, 0);			// Used for drawing entities.
        private Bgr aoiColor = new Bgr(Color.Red);

        
		private MCvFont imgFont = new MCvFont(FONT.CV_FONT_HERSHEY_COMPLEX_SMALL, 0.5, 0.5);

        private AreasOfInterest myAOIs = new AreasOfInterest();

		private string savePath = "";			// Path of the currently active profile.
		//===========================================================
		#endregion

		#region [Global]
		//======[Global]=============================================
		/// <summary>
		/// The text that is shown at the bottom of the window to indicate its status.
		/// Can be used to tell the user what is happening in the background
		/// or which are the next steps to be done.
		/// </summary>
		public String Status {
			get { return statusText.Text; }
			set { statusText.Text = value;  }
		}

		/// <summary>
		/// Completely reloads the merger list to force a refresh.
		/// Neccessary whenever a new merger has been added.
		/// </summary>
		public void RefreshMergerList() {
			// set it to null to force a full reload
			mergerStateView.DataSource = null;
			mergerStateView.DataSource = Program.Mergers;
			if(mergerStateView.Columns.Contains("Running")) mergerStateView.Columns.Remove("Running");
			mergerStateView.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.DisableResizing;
			mergerStateView.RowHeadersWidthSizeMode = DataGridViewRowHeadersWidthSizeMode.DisableResizing;
			mergerStateView.Columns[0].Width = 140;
			mergerStateView.Columns[1].Width = 45;
			mergerStateView.Columns[2].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
		}
		/// <summary>
		/// Draws the wall map depending on whether a pixel of the realMap is threathed as wall.
		/// </summary>
		public void CreateWallMap() {
			ThreadPool.QueueUserWorkItem(delegate {
				if (realMap == null) return; // nothing to do
				wallMap = realMap.Clone();
				float threshold = 255 * Program.WallThreshold;
				wallMap._ThresholdBinary(new Bgr(threshold, threshold, threshold), new Bgr(255, 255, 255));
			});
		}

		/// <summary>
		/// Adds an entry to the log that is correctly formatted.
		/// </summary>
		/// <param name="text">The message that should be logged.</param>
		public void Log(String text) {
			outputText.BeginInvoke(new MethodInvoker(delegate() {
				DateTime time = DateTime.Now;
				outputText.AppendText("[" + time.Hour + ":" + (time.Minute < 10 ? "0" : "") + time.Minute + "] " + text + Environment.NewLine);
				outputText.ScrollToCaret();
			}));
		}
		/// <summary>
		/// Adds an entry to the log that stands for a warning.
		/// Warnings are indicated by a yellow symbol and stand for
		/// unexpected but not problematic behaviour.
		/// </summary>
		/// <param name="text">The text that should be logged as a warning.</param>
		public void Warn(String text) {
			outputText.BeginInvoke(new MethodInvoker(delegate() {
				DateTime time = DateTime.Now;
				outputText.AppendText("[" + time.Hour + ":" + (time.Minute < 10 ? "0" : "") + time.Minute + "] (*) " + text + Environment.NewLine);
				outputText.ScrollToCaret();
				tabPage3.ImageIndex = 4;
			}));
		}
		/// <summary>
		/// Adds an entry to the log that stands for an error.
		/// Errors are indicated by a red symbol and stand for
		/// unexpected and problematic behaviour.
		/// </summary>
		/// <param name="text">The text that should be logged as an error.</param>
		public void Error(String text) {
			outputText.BeginInvoke(new MethodInvoker(delegate() {
				DateTime time = DateTime.Now;
				outputText.AppendText("[" + time.Hour + ":" + (time.Minute < 10 ? "0" : "") + time.Minute + "] [!] " + text + Environment.NewLine);
				outputText.ScrollToCaret();
				tabPage3.ImageIndex = 5;
			}));
		}

		/// <summary>
		/// Updates the coverage map.
		/// Neccessary whenever a cam or the preferences changed.
		/// </summary>
		/// <param name="cam">The cam that should be updated at the coverage map. Use <c>null</c> if the whole map should be updated.</param>
		/// <param name="add"><c>false</c> if the cam has been removed, <c>true</c> if added.</param>
		public void UpdateCoverageMap(SensorCam cam, bool add) {
			Program.SetStatus("Updating map...");
			Invoke(new MethodInvoker(delegate { toolStripProgressBar.Visible = true; }));

			ThreadPool.QueueUserWorkItem(delegate {
                if (cam != null) FloorPlan.UpdateCamAtCoverageMap(cam, add);
                else {
                    FloorPlan.InitializeMapCoverage();
                    coverageMap = new Image<Bgr, byte>(FloorPlan.DrawCoverageMap());
                }
				Invoke(new MethodInvoker(delegate { toolStripProgressBar.Visible = false; }));
				Program.SetStatus("Running...");
			});
		}

		/// <summary>
		/// Starts the network and updates the GUI according to it.
		/// </summary>
		public void StartNetwork() {
			startServerToolStripMenuItem.Image = Properties.Resources.drive_delete;
			startServerToolStripMenuItem.Text = "Stop Network";
			NetworkConnector.StartNetwork();
			networkInactivePanel.Visible = false;
			InitializeNetworkPanel();
			networkActivePanel.Visible = true;
			networkActive = true;
		}
		/// <summary>
		/// Stops the network and updates the GUI according to it.
		/// </summary>
		public void StopNetwork() {
			startServerToolStripMenuItem.Image = Properties.Resources.drive_link;
			startServerToolStripMenuItem.Text = "Start Network";
			networkActive = false;
			networkInactivePanel.Visible = true;
			networkActivePanel.Visible = false;
			NetworkConnector.StopNetwork();
		}
		//===========================================================
		#endregion

		#region [Events]
		//======[Events]=============================================
		private void MainWindow_Load(object sender, EventArgs e) {
			mapBox = imageBig;
			camBox = imageSmall;
			camListPanels = new List<Panel>();
			performanceSelector.SelectedIndex = 0;

			// Disable unavailable elements
			addCameraToolStripMenuItem.Enabled = false;
			addCameraToolStripButton.Enabled = false;
			cameraToolStripMenuItem.Enabled = false;
			saveProfileToolStripMenuItem.Enabled = false;
			saveProfileAsToolStripMenuItem.Enabled = false;
            optimizePlacementToolStripMenuItem.Enabled = false;
			motionDebuggingToolStripMenuItem.Enabled = false;

			// Initialize merger list
			mergerStateView.DataSource = Program.Mergers;

			// Load standard profile if set
			if (Program.ProfilePath != "") {
				savePath = Program.ProfilePath;
				addCameraToolStripButton.Enabled = true;
				addCameraToolStripMenuItem.Enabled = true;
				saveProfileToolStripMenuItem.Enabled = true;
				saveProfileAsToolStripMenuItem.Enabled = true;
				LoadProfile(@savePath);
			}
			
			// --- Thread Handling ---

			// Thread for updating the two image boxes
			Thread updateImageBoxesThread = new Thread(DrawImageBoxes);
			updateImageBoxesThread.IsBackground = true;
			updateImageBoxesThread.Start();

			// Thread for re-calculating statistics
			Thread updateStatisticsThread = new Thread(UpdateStatistics);
			updateStatisticsThread.IsBackground = true;
			updateStatisticsThread.Start();

			// End threads before exiting
			FormClosing += delegate {
				if (networkActive)
					NetworkConnector.StopNetwork();
				updateImageBoxesThread.Abort();
				updateStatisticsThread.Abort();
			};

            
		}

		private void MainWindow_Resize(object sender, EventArgs e) {
			imageBig.Height = Height - 110;
			imageBig.Width = Width - 320;
			tabPanel.Height = Height - 340;
			tabPanel.Left = Width - 316;
			addCamPanel.Height = Height - 340;
			addCamPanel.Left = Width - 316;
			imageSmall.Top = Height - 285;
			imageSmall.Left = Width - 316;

			camScrollPanel.Height = Height - 366;
			statisticsText.Height = Height - 378;
			outputText.Height = Height - 372;
		}

		private void tabPanelResetStatusIcon(object sender, EventArgs e) {
			tabPage3.ImageIndex = 3;
		}


		// --- Menu ---
		private void newProfileToolStripMenuItem_Click(object sender, EventArgs e) {
			ClearProfile();
			addCameraToolStripButton.Enabled = false;
			addCameraToolStripMenuItem.Enabled = false;
			saveProfileToolStripMenuItem.Enabled = false;
			saveProfileAsToolStripMenuItem.Enabled = false;
			startServerToolStripMenuItem.Enabled = false;
			startNetworkButton.Enabled = false;
			networkNoMapWarning.Visible = true;
			analyseCoverageToolStripMenuItem.Enabled = false;
            optimizePlacementToolStripMenuItem.Enabled = false;
			savePath = "";
		}
		private void loadProfileToolStripMenuItem_Click(object sender, EventArgs e) {
			OpenFileDialog ofd = new OpenFileDialog();
			ofd.Title = "Please choose a profile to load";
			ofd.Filter = "Profile files|*.profile";
			if (ofd.ShowDialog() == DialogResult.OK) {
				savePath = ofd.FileName;
				addCameraToolStripButton.Enabled = true;
				addCameraToolStripMenuItem.Enabled = true;
				saveProfileToolStripMenuItem.Enabled = true;
				saveProfileAsToolStripMenuItem.Enabled = true;
				LoadProfile(@savePath);
			}
		}
		private void saveProfileToolStripMenuItem_Click(object sender, EventArgs e) {
			SaveProfile(@savePath);
		}
		private void saveProfileAsToolStripMenuItem_Click(object sender, EventArgs e) {
			SaveFileDialog sfd = new SaveFileDialog();
			sfd.Title = "Please enter a name for the profile";
			sfd.Filter = "Profile files|*.profile";
			if (sfd.ShowDialog() == DialogResult.OK) {
				savePath = sfd.FileName;
				SaveProfile(@savePath);
				saveProfileToolStripMenuItem.Enabled = true;
			}
		}
		private void startServerToolStripMenuItem_Click(object sender, EventArgs e) {
			if (networkActive) {
				StopNetwork();
			} else {
				StartNetwork();
			}
		}
		private void quitToolStripMenuItem_Click(object sender, EventArgs e) {
			Application.Exit();
		}

		private void addCameraToolStripMenuItem_Click(object sender, EventArgs e) {
			InitializeAddCamPanel();
			addCamPanel.Visible = true;
			tabPanel.Visible = false;
		}
		private void loadMapToolStripMenuItem_Click(object sender, EventArgs e) {
			Bitmap toLoad = ChooseMap();
			if (toLoad != null)
				LoadMap(toLoad);
		}
		private void analyseCoverageToolStripMenuItem_Click(object sender, EventArgs e) {
			CoverageMapWindow covMapWindow = new CoverageMapWindow(coverageMap.Bitmap);
			covMapWindow.ShowDialog(this);
		}
		private void optimizePlacementToolStripMenuItem_Click(object sender, EventArgs e) {
			OptimizeWindow opt = new OptimizeWindow(realMap);
			opt.ShowDialog(this);
		}
		private void preferencesToolStripMenuItem_Click(object sender, EventArgs e) {
			PreferencesWindow prefWindow = new PreferencesWindow();
			prefWindow.ShowDialog(this);
		}

		private void switchViewToolStripMenuItem_Click(object sender, EventArgs e) {
			SwitchView();
		}
		private void coverageViewToolStripMenuItem_Click(object sender, EventArgs e) {
			ToggleCoverageView();
		}
		private void drawWallsToolStripMenuItem_Click(object sender, EventArgs e) {
			ToggleDrawWalls();
		}
		private void drawCamerasToolStripMenuItem_Click(object sender, EventArgs e) {
			ToggleDrawCams();
		}
		private void drawRaysToolStripMenuItem_Click(object sender, EventArgs e) {
			ToggleDrawRays();
		}
		private void drawEntitiesToolStripMenuItem_Click(object sender, EventArgs e) {
			ToggleDrawEntities();
		}
        private void drawAOIToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ToggleDrawAOI();
        }
		private void showIDToolStripMenuItem_Click(object sender, EventArgs e) {
			ToggleShowID();
		}
		private void showReliabilityToolStripMenuItem_Click(object sender, EventArgs e) {
			ToggleShowReliability();
		}
		private void showOriginsToolStripMenuItem_Click(object sender, EventArgs e) {
			ToggleShowOrigin();
		}
		private void showPositionToolStripMenuItem_Click(object sender, EventArgs e) {
			ToggleShowPosition();
		}

		private void detachImageToolStripMenuItem_Click(object sender, EventArgs e) {
			CamView camView = new CamView(selectedCam);
			camView.Show();
		}
		private void propertiesToolStripMenuItem_Click(object sender, EventArgs e) {
			CamPropertiesWindow props = new CamPropertiesWindow(selectedCam);
			props.Show();
		}
        private void toolStripMenuItem1_Click(object sender, EventArgs e) {
            selectedCam.Device.AutoExposure = false;
            selectedCam.Device.AutoGain = false;
            selectedCam.Device.AutoWhiteBalance = false;
			Image<Bgr, Byte> tmpRefFrame = selectedCam.Frame;
			Image<Bgr, Byte> tmpRefFrameWarp = tmpRefFrame.WarpPerspective(selectedCam.TransMatrix, Program.FrameWidth, Program.FrameHeight, Emgu.CV.CvEnum.INTER.CV_INTER_LINEAR, Emgu.CV.CvEnum.WARP.CV_WARP_FILL_OUTLIERS, new Bgr(0, 0, 0));
			selectedCam.RefFrameGray = tmpRefFrameWarp.Convert<Gray, Byte>();
			NotificationWindow notificationWindow = new NotificationWindow(selectedCam);
			notificationWindow.Show();
        }

        private void motionDebuggingToolStripMenuItem_Click(object sender, EventArgs e) {
            Detection.MotionDebugWindow motionDebugWindow = new Detection.MotionDebugWindow();
            motionDebugWindow.Show();
        }

		private void aboutToolStripMenuItem_Click(object sender, EventArgs e) {
			AboutWindow about = new AboutWindow();
			about.ShowDialog(this);
		}
		private void showHelpToolStripMenuItem_Click(object sender, EventArgs e) {
            Help.ShowHelp(this, "help\\Help.chm");
		}

		// --- Tool Strip ---
		private void addCameraToolStripButton_Click(object sender, EventArgs e) {
			InitializeAddCamPanel();
			addCamPanel.Visible = true;
			tabPanel.Visible = false;
		}
		private void changeMapToolStripButton_Click(object sender, EventArgs e) {
			Bitmap toLoad = ChooseMap();
			if (toLoad != null)
				LoadMap(toLoad);
		}
		private void coverageViewToolStripButton_Click(object sender, EventArgs e) {
			ToggleCoverageView();
		}
		private void drawWallsToolStripButton_Click(object sender, EventArgs e) {
			ToggleDrawWalls();
		}
		private void drawCamerasToolStripButton_Click(object sender, EventArgs e) {
			ToggleDrawCams();
		}
		private void drawRaysToolStripButton_Click(object sender, EventArgs e) {
			ToggleDrawRays();
		}
		private void drawEntitiesToolStripButton_Click(object sender, EventArgs e) {
			ToggleDrawEntities();
		}
		private void switchViewToolStripButton_Click(object sender, EventArgs e) {
			SwitchView();
		}
		

		// --- Add Cam Panel ---
		private void addCamCancel_Click(object sender, EventArgs e) {
			tabPanel.Visible = true;
			addCamPanel.Visible = false;
			//selectedNewCam.Dispose();
			//selectedNewCam = null;
		}
		private void addCamAdd_Click(object sender, EventArgs e) {
			tabPanel.Visible = true;
			addCamPanel.Visible = false;
			AddNewCam();
		}
		private void addCamDeviceSelect_SelectedIndexChanged(object sender, EventArgs e) {
			addCamSelectedIndex = addCamDeviceSelect.SelectedIndex;
		}

		private void addCamPosU_Click(object sender, EventArgs e) {
			addCamY.Value += addCamTransStep.Value;
			if (addCamY.Value - addCamTransStep.Value >= 0) {
				addCamPosDR.Enabled = (addCamX.Value + addCamTransStep.Value) <= FloorPlan.MaxX;
				addCamPosD.Enabled = true;
				addCamPosDL.Enabled = (addCamX.Value - addCamTransStep.Value) >= 0;
			}
			if (addCamY.Value + addCamTransStep.Value > FloorPlan.MaxY) {
				addCamPosUR.Enabled = false;
				addCamPosU.Enabled = false;
				addCamPosUL.Enabled = false;
			}
		}
		private void addCamPosUR_Click(object sender, EventArgs e) {
			addCamPosU_Click(sender, e);
			addCamPosR_Click(sender, e);
		}
		private void addCamPosR_Click(object sender, EventArgs e) {
			addCamX.Value += addCamTransStep.Value;
			if (addCamX.Value - addCamTransStep.Value >= 0) {
				addCamPosUL.Enabled = (addCamY.Value + addCamTransStep.Value) <= FloorPlan.MaxY;
				addCamPosL.Enabled = true;
				addCamPosDL.Enabled = (addCamY.Value - addCamTransStep.Value) >= 0;
			}
			if (addCamX.Value + addCamTransStep.Value > FloorPlan.MaxX) {
				addCamPosUR.Enabled = false;
				addCamPosR.Enabled = false;
				addCamPosDR.Enabled = false;
			}
		}
		private void addCamPosDR_Click(object sender, EventArgs e) {
			addCamPosD_Click(sender, e);
			addCamPosR_Click(sender, e);
		}
		private void addCamPosD_Click(object sender, EventArgs e) {
			addCamY.Value -= addCamTransStep.Value;
			if (addCamY.Value - addCamTransStep.Value < 0) {
				addCamPosDR.Enabled = false;
				addCamPosD.Enabled = false;
				addCamPosDL.Enabled = false;
			}
			if (addCamY.Value + addCamTransStep.Value <= FloorPlan.MaxY) {
				addCamPosUR.Enabled = (addCamX.Value + addCamTransStep.Value) <= FloorPlan.MaxX;
				addCamPosU.Enabled = true;
				addCamPosUL.Enabled = (addCamX.Value - addCamTransStep.Value) >= 0;
			}
		}
		private void addCamPosDL_Click(object sender, EventArgs e) {
			addCamPosD_Click(sender, e);
			addCamPosL_Click(sender, e);
		}
		private void addCamPosL_Click(object sender, EventArgs e) {
			addCamX.Value -= addCamTransStep.Value;
			if (addCamX.Value - addCamTransStep.Value < 0) {
				addCamPosUL.Enabled = false;
				addCamPosL.Enabled = false;
				addCamPosDL.Enabled = false;
			}
			if (addCamX.Value + addCamTransStep.Value <= FloorPlan.MaxX) {
				addCamPosUR.Enabled = (addCamY.Value + addCamTransStep.Value) <= FloorPlan.MaxY; ;
				addCamPosR.Enabled = true;
				addCamPosDR.Enabled = (addCamY.Value - addCamTransStep.Value) >= 0; ;
			}
		}
		private void addCamPosUL_Click(object sender, EventArgs e) {
			addCamPosU_Click(sender, e);
			addCamPosL_Click(sender, e);
		}

		private void addCamX_ValueChanged(object sender, EventArgs e) {
			MethodInvoker processChange = delegate() {
				if (addCamX.Value - addCamTransStep.Value >= 0) {
					addCamPosUL.Enabled = (addCamY.Value + addCamTransStep.Value) <= FloorPlan.MaxY;
					addCamPosL.Enabled = true;
					addCamPosDL.Enabled = (addCamY.Value - addCamTransStep.Value) >= 0;
				}
				if (addCamX.Value + addCamTransStep.Value > FloorPlan.MaxX) {
					addCamPosUR.Enabled = false;
					addCamPosR.Enabled = false;
					addCamPosDR.Enabled = false;
				}
				if (addCamX.Value - addCamTransStep.Value < 0) {
					addCamPosUL.Enabled = false;
					addCamPosL.Enabled = false;
					addCamPosDL.Enabled = false;
				}
				if (addCamX.Value + addCamTransStep.Value <= FloorPlan.MaxX) {
					addCamPosUR.Enabled = (addCamY.Value + addCamTransStep.Value) <= FloorPlan.MaxY; ;
					addCamPosR.Enabled = true;
					addCamPosDR.Enabled = (addCamY.Value - addCamTransStep.Value) >= 0; ;
				}
			};
			Invoke(processChange);
		}
		private void addCamY_ValueChanged(object sender, EventArgs e) {
			MethodInvoker processChange = delegate() {
				if (addCamY.Value - addCamTransStep.Value >= 0) {
					addCamPosDR.Enabled = (addCamX.Value + addCamTransStep.Value) <= FloorPlan.MaxX;
					addCamPosD.Enabled = true;
					addCamPosDL.Enabled = (addCamX.Value - addCamTransStep.Value) >= 0;
				}
				if (addCamY.Value + addCamTransStep.Value > FloorPlan.MaxY) {
					addCamPosUR.Enabled = false;
					addCamPosU.Enabled = false;
					addCamPosUL.Enabled = false;
				}
				if (addCamY.Value - addCamTransStep.Value < 0) {
					addCamPosDR.Enabled = false;
					addCamPosD.Enabled = false;
					addCamPosDL.Enabled = false;
				}
				if (addCamY.Value + addCamTransStep.Value <= FloorPlan.MaxY) {
					addCamPosUR.Enabled = (addCamX.Value + addCamTransStep.Value) <= FloorPlan.MaxX;
					addCamPosU.Enabled = true;
					addCamPosUL.Enabled = (addCamX.Value - addCamTransStep.Value) >= 0;
				}
			};
			Invoke(processChange);
		}
		private void addCamTransStep_ValueChanged(object sender, EventArgs e) {
			addCamX_ValueChanged(sender, e);
			addCamY_ValueChanged(sender, e);
		}

		private void addCamOriU_Click(object sender, EventArgs e) {
			addCamOrientation.Value = 90;
		}
		private void addCamOriUR_Click(object sender, EventArgs e) {
			addCamOrientation.Value = 45;
		}
		private void addCamOriR_Click(object sender, EventArgs e) {
			addCamOrientation.Value = 0;
		}
		private void addCamOriDR_Click(object sender, EventArgs e) {
			addCamOrientation.Value = 315;
		}
		private void addCamOriD_Click(object sender, EventArgs e) {
			addCamOrientation.Value = 270;
		}
		private void addCamOriDL_Click(object sender, EventArgs e) {
			addCamOrientation.Value = 225;
		}
		private void addCamOriL_Click(object sender, EventArgs e) {
			addCamOrientation.Value = 180;
		}
		private void addCamOriUL_Click(object sender, EventArgs e) {
			addCamOrientation.Value = 135;
		}

		private void addCamOrientation_ValueChanged(object sender, EventArgs e) {
			if (addCamOrientation.Value < 0) addCamOrientation.Value = 359;
			if (addCamOrientation.Value > 359) addCamOrientation.Value = 0;
		}

		private void addCamPitchBar_Scroll(object sender, EventArgs e) {
			addCamPitch.Value = addCamPitchBar.Value;
		}
		private void addCamPitch_ValueChanged(object sender, EventArgs e) {
			addCamPitchBar.Value = (int)addCamPitch.Value;
		}

		// --- Statistics ---
		private void performanceSelector_SelectedIndexChanged(object sender, EventArgs e) {
			performanceChart.BeginInvoke(new MethodInvoker(delegate() {
				performanceChart.ChartAreas["area"].AxisY.Maximum = 100;
				performanceChart.ChartAreas["area"].AxisY.MajorGrid.Interval = 100;

				performanceChart.Series["MB RAM (process)"].Enabled = false;
				performanceChart.Series["% CPU (total)"].Enabled = false;
				if (performanceSelector.SelectedItem != null)
					performanceChart.Series[performanceSelector.SelectedItem.ToString()].Enabled = true;
			}));
		}

		// --- Network ---
		private void startNetworkButton_Click(object sender, EventArgs e) {
			StartNetwork();
		}

        private void allRefButton_Click(object sender, EventArgs e)
        {
            foreach (SensorCam cam in Program.Cameras)
            {
                cam.Device.AutoExposure = false;
                cam.Device.AutoGain = false;
                cam.Device.AutoWhiteBalance = false;
                Image<Bgr, Byte> tmpRefFrame = cam.Frame;
                Image<Bgr, Byte> tmpRefFrameWarp = tmpRefFrame.WarpPerspective(cam.TransMatrix, Program.FrameWidth, Program.FrameHeight, Emgu.CV.CvEnum.INTER.CV_INTER_LINEAR, Emgu.CV.CvEnum.WARP.CV_WARP_FILL_OUTLIERS, new Bgr(0, 0, 0));
                cam.RefFrameGray = tmpRefFrameWarp.Convert<Gray, Byte>();
            }
        }

		//===========================================================
		#endregion

		#region [Methods]
		//======[Methods]============================================
		/// <summary>
		/// Creates a new FileDialog asking the user to choose an image as the map to be loaded.
		/// </summary>
		/// <returns>The choosen image.</returns>
		private Bitmap ChooseMap() {
			OpenFileDialog ofd = new OpenFileDialog();
			ofd.Filter = "Image files|*.jpg;*.jpeg;*.gif;*.bmp;*.png;*.tif;*.tiff";
			if (ofd.ShowDialog() == DialogResult.OK) {
				return new Bitmap(Image.FromFile(ofd.FileName));
			}
			return null;
		}
		/// <summary>
		/// Loads the given Bitmap as the new map.
		/// As a part of the loading, wallMap and coverageMap are created
		/// and illegal placed cams are removed.
		/// Also handles all GUI updates during the progress.
		/// </summary>
		/// <param name="newMap">The image that should be loaded as the new map.</param>
		private void LoadMap(Bitmap newMap) {
			Program.SetStatus("Loading new map...");
			toolStripProgressBar.Visible = true;

			Task task =  Task.Factory.StartNew(() => {
				// stop network if it is running and all cams
				if (networkActive) StopNetwork();
				Program.CamPause();

				FloorPlan.Map = newMap;

				// test which cams are out of bounds of the new map
				bool allInBounds = true;
				for (int i = 0; i < Program.Cameras.Count; i++) {
					SensorCam cam = Program.Cameras[i];
					if (!FloorPlan.InBounds(new HCoord(cam.X, cam.Y))) {
						allInBounds = false;
						// remove cam from gui
						int toRemoveIndex = camListPanels.FindIndex(new Predicate<Panel>(delegate(Panel current) {
							return ((SensorCam)current.Tag).Equals(cam);
						}));
						Invoke(new MethodInvoker(delegate {
							camListPanels[toRemoveIndex].Dispose();
							camListPanels.RemoveAt(toRemoveIndex);
						}));
						// move up all lower panels
						for (int j = toRemoveIndex; j < camListPanels.Count; j++)
							camListPanels[j].Location = new Point(
									camListPanels[j].Location.X,
									camListPanels[j].Location.Y - 26);

						// remove cam from program
						Program.Cameras.RemoveAt(i--);
						cam.Dispose();
					}
				}

				// load new map
				Image<Bgr, Byte> newImage = new Image<Bgr, Byte>(newMap);

				realMap = newImage.Clone();

				Program.SetStatus("Initializing map...");

				CreateWallMap();
				coverageMap = new Image<Bgr, Byte>(FloorPlan.DrawCoverageMap());

				Invoke(new MethodInvoker(delegate {
					addCameraToolStripMenuItem.Enabled = true;
					addCameraToolStripButton.Enabled = true;
					saveProfileAsToolStripMenuItem.Enabled = true;
					startServerToolStripMenuItem.Enabled = true;
					startNetworkButton.Enabled = true;
					networkNoMapWarning.Visible = false;
					analyseCoverageToolStripMenuItem.Enabled = true;
					toolStripProgressBar.Visible = false;
				}));

				Program.CamResume();

				if (Program.Cameras.Count > 0)
					Program.SetStatus("Running...");
				else
					Program.SetStatus("Please add a camera.");

				Log("New map loaded");
				if (savePath.Equals("")) Text = "Indoor Localization";
				else Invoke(new MethodInvoker(delegate { Text = Path.GetFileNameWithoutExtension(@savePath) + "* - Indoor Localization"; }));

				// show warning if a cam was removed
				if (!allInBounds) {
					saveProfileToolStripMenuItem.Enabled = false;
					MessageBox.Show(
						"Not all cameras were in bounds of the new map." + Environment.NewLine
						+ "Those cameras have been removed.",
						"Warning",
						MessageBoxButtons.OK,
						MessageBoxIcon.Warning,
						MessageBoxDefaultButton.Button1
					);
					Warn("Some cameras were out of bounds");
				}

			});

			task.Wait(1000);
		}

		// Thread
		/// <summary>
		/// Draws the images (map and cam) according to the settings
		/// and updates their corresponding boxes.
		/// Should be started as a background thread.
		/// </summary>
		private void DrawImageBoxes() {
			while (true) {
				// draw map
				//----------
				Image<Bgr, Byte> mapImage;

				// choose the background image
				if (drawWalls)			mapImage = (wallMap != null)? wallMap.Clone() : null;
				else if (drawCoverage)	mapImage = (coverageMap != null)? coverageMap.Clone() : null;
				else					mapImage = (realMap != null)? realMap.Clone() : null;

				if (mapImage != null) {

					#region DrawCams
					// ---[ DrawCams ]-------------------------------
					if (drawCams) {
						// draw existing cams
						foreach (SensorCam cam in Program.Cameras) {
							Bgr color;
							// choose color of the cam depending on its state
							if (cam.Equals(selectedCam))	color = camSelectedColor;
							else if (cam.IsPaused())		color = camPausedColor;
							else							color = camDefaultColor;

							// draw the cam
							mapImage.Draw(new Rectangle(cam.X - 1, FloorPlan.MaxY - cam.Y - 1, 3, 3), color, 1);

							// draw the direction pointer of the cam
							float deltaX = (float)Math.Cos(cam.Orientation / 180 * Math.PI);
							float deltaY = (float)Math.Sin(cam.Orientation / 180 * Math.PI);
							HCoord position = new HCoord(cam.X, cam.Y);
							for (int i = 0; i < 8; i++) {
								mapImage.Draw(new Rectangle(position.X, FloorPlan.MaxY - position.Y, 1, 1), color, 1);
								position.Translate(deltaX, deltaY);
							}
						}
					}
					#endregion

					#region DrawCamToAdd
					// ---[ DrawCamToAdd ]---------------------------
					if (addCamPanel.Visible && Program.Devices.Count > 0) {
						// draw cam to add
						mapImage.Draw(new Rectangle((int)addCamX.Value - 1, (int)(FloorPlan.MaxY - addCamY.Value) - 1, 3, 3), camAddColor, 1);
						
						// draw direction pointer
						float deltaX = (float)Math.Cos((float)addCamOrientation.Value / 180 * Math.PI);
						float deltaY = (float)Math.Sin((float)addCamOrientation.Value / 180 * Math.PI);
						HCoord position = new HCoord((int)addCamX.Value, (int)addCamY.Value);
						for (int i = 0; i < 8; i++) {
							mapImage.Draw(new Rectangle(position.X, FloorPlan.MaxY - position.Y, 1, 1), camAddColor, 1);
							position.Translate(deltaX, deltaY);
						}
						mapImage.Draw(new CircleF(new PointF((int)addCamX.Value, (int)(FloorPlan.MaxY - addCamY.Value)), 7), new Bgr(Color.Red), 1);
					}
					#endregion

					#region DrawRays
					// ---[ DrawRays ]-------------------------------
					if (drawRays) {
						// collect all currently existing rays
						List<SensorRay> rays = new List<SensorRay>();
						foreach (SensorMerger merger in Program.Mergers)
							if(merger.IsRunning()) rays.AddRange(merger.GetRaysTS());

						// draw each ray
						foreach (SensorRay ray in rays) {
							float deltaX = (float)Math.Cos(ray.Angle / 180 * Math.PI);
							float deltaY = (float)Math.Sin(ray.Angle / 180 * Math.PI);
							HCoord position = new HCoord(ray.Origin.X, ray.Origin.Y);
							while (FloorPlan.IsValidPosition(position)) {
								//mapImage.Draw(new Rectangle(position.X, FloorPlan.MaxY - position.Y, 1, 1), rayColor, 1);
                                mapImage.Draw(new Rectangle(position.X, FloorPlan.MaxY - position.Y, 1, 1), ray.AvgColor, 1);
								position.Translate(deltaX, deltaY);
							}
							//mapImage.Draw(""+ray.EstimatedDistanceIndicator, ref imgFont, new Point(position.X, FloorPlan.MaxY - position.Y), rayColor);
						}
					}
					#endregion

					#region DrawEntities
					// ---[ DrawEntities ]---------------------------
					if (drawEntities) {
						// collect all found entities
						List<Entity> entities = new List<Entity>();
						foreach(SensorMerger merger in Program.Mergers)
							if (merger.IsRunning()) entities.AddRange(merger.GetEntitiesTS());
						if (networkActive) entities.AddRange(NetworkConnector.GetEntitiesTS());

						// draw each entity
						foreach (Entity entity in entities) {
							if (entity.Reliability < 10) continue; // only draw entities with a reliability > 10%

							// calculate color depending on reliability
							double colorFactor = entity.Reliability/100.0;
							Bgr color = new Bgr(
											entityColor.Blue * colorFactor + entityColor.Green * (1 - colorFactor),
											entityColor.Green,
											entityColor.Red * colorFactor + entityColor.Green * (1 - colorFactor));

                            if (myAOIs.isInAOI(entity.X, entity.Y).Count > 0)
                            {
                                color = new Bgr(Color.Pink);
                            }
							
							// draw entity
							mapImage.Draw(new Rectangle(entity.X - 1, FloorPlan.MaxY - entity.Y - 1, 3, 3), color, 3);
							
							// draw additional information of the entity depending on the settings
							if (showID || showReliability || showOrigin || showPosition) {
								int yOffset = 2;
								if (showID) {
									mapImage.Draw("#" + entity.ID, ref imgFont, new Point(entity.X + 5, FloorPlan.MaxY - entity.Y + yOffset), color);
									yOffset += 8;
								}
								if (showReliability) {
									mapImage.Draw("R=" + entity.Reliability, ref imgFont, new Point(entity.X + 5, FloorPlan.MaxY - entity.Y + yOffset), color);
									yOffset += 8;
								}
								if (showPosition) {
									mapImage.Draw("(" + entity.X + "/" + entity.Y + ")", ref imgFont, new Point(entity.X + 5, FloorPlan.MaxY - entity.Y + yOffset), color);
									yOffset += 8;
								}
								if (showOrigin) {
									if (entity.Sensors.Count <= 1) {
										// network entity
										mapImage.Draw("Network "+entity.NetworkID, ref imgFont, new Point(entity.X + 5, FloorPlan.MaxY - entity.Y + yOffset), color);
										yOffset += 8;
									} else {
										// own entity
										entity.Sensors.Sort(new Comparison<SensorCam>(delegate(SensorCam first, SensorCam second) {
											return String.Compare(first.Name, second.Name, false);
										}));
										foreach (SensorCam originSensor in entity.Sensors) {
											mapImage.Draw(originSensor.Name, ref imgFont, new Point(entity.X + 5, FloorPlan.MaxY - entity.Y + yOffset), color);
											yOffset += 8;
										}
									}
								}
								
							}
						}
					}
					#endregion

                    #region DrawAOI
                    // ---[ DrawAOI ]-------------------------------
                    if (drawAOI)
                    {
                        
                        foreach (AOI a in myAOIs.areasOfInterest)
                        {
                            Rectangle drawRect = a.getDrawingRectangle(FloorPlan.MaxY);
                            mapImage.Draw(drawRect, aoiColor, 1);
                            mapImage.Draw(a.ID.ToString(), ref imgFont, new Point(drawRect.X + 10, drawRect.Y + 10), aoiColor);

                        }
                    }
                    #endregion

				}

				// draw cam image
				if (selectedCam != null && !addCamPanel.Visible) {
					// draw the selected cam
					camImage = selectedCam.Frame;
				} else {
					if (addCamPanel.Visible && Program.Devices.Count > 0) {
						// draw the cam that should be added
						try {
							if (Program.Devices[addCamSelectedIndex].Grab())
								camImage = Program.Devices[addCamSelectedIndex].RetrieveBgrFrame();
						} catch (ArgumentOutOfRangeException) { }

					} else {
						// no cam image has to be drawn
						camImage = null;
					}
				}

				if(mapBox != null) mapBox.Image = mapImage;
				if(camBox != null) camBox.Image = camImage;
				Thread.Sleep(100);	// wait to save cpu-time
			}
		}
		// Thread
		/// <summary>
		/// Queries the state of the operating system and the application
		/// to update the values at the statistics and network panels.
		/// Should be started as a background thread.
		/// </summary>
		private void UpdateStatistics() {
			while (true) {
				// Update CPU
				performanceChart.BeginInvoke(new MethodInvoker(delegate() {
					int cpu = (int)Math.Min(100, cpuCounter.NextValue());
					if (performanceChart.Series["% CPU (total)"].Points.Count >= 60)
						performanceChart.Series["% CPU (total)"].Points.RemoveAt(0);
					performanceChart.Series["% CPU (total)"].Points.AddY(cpu);
					cpuUsageValue.Text = cpu+" %";
				}));
				// Update RAM
				ramUsageValue.BeginInvoke(new MethodInvoker(delegate() {
					int ram = (int)Math.Round(ramCounter.NextValue() / 1024 / 1024);
					if (performanceChart.Series["MB RAM (process)"].Points.Count >= 60)
						performanceChart.Series["MB RAM (process)"].Points.RemoveAt(0);
					performanceChart.Series["MB RAM (process)"].Points.AddY(ram);
					if (performanceChart.Series["MB RAM (process)"].Enabled) {
						performanceChart.ChartAreas["area"].AxisY.Maximum =
							performanceChart.Series["MB RAM (process)"].Points.FindMaxByValue("Y1").YValues[0] * 1.2;
						performanceChart.ChartAreas["area"].AxisY.MajorGrid.Interval = 
							performanceChart.ChartAreas["area"].AxisY.Maximum;
					}
					ramUsageValue.Text = ram + " MB";
				}));
				// Update Threads
				threadNumValue.BeginInvoke(new MethodInvoker(delegate() {
					threadNumValue.Text = ""+(int)threadCounter.NextValue();
				}));

				// update merger state
				mergerStateView.BeginInvoke(new MethodInvoker(delegate() {
					mergerStateView.Invalidate();
				}));

				// update network state
				if (networkActive) {
					otherApplicationsText.BeginInvoke(new MethodInvoker(delegate() {
						otherApplicationsText.Text = "Connected to " + NetworkConnector.EndPoints + " other application" + (NetworkConnector.EndPoints==1? "":"s") + ".";
					}));
					remoteMergersText.BeginInvoke(new MethodInvoker(delegate() {
						remoteMergersText.Text = Program.RemoteMergers.Count + " remote merger" + (Program.RemoteMergers.Count == 1 ? "" : "s") + " available.";
					}));
					entitiesReceivedText.BeginInvoke(new MethodInvoker(delegate() {
						entitiesReceivedText.Text = NetworkConnector.NumberOfEntities + " entit" + (NetworkConnector.NumberOfEntities == 1 ? "y" : "ies") + " of " + (NetworkConnector.EndPoints == 1 ? "the other application" : "other applications") + " received.";
					}));
				}

				Thread.Sleep(500);
			}
		}
		
		/// <summary>
		/// Deletes the current profile and creates the initial state of the application.
		/// </summary>
		private void ClearProfile() {
			// stop network if it is running
			if (networkActive) StopNetwork();

			// clear GUI cam list
			selectedCamPanel = null;
			foreach (Panel panel in camListPanels) {
				panel.Visible = false;
				panel.Dispose();
			}
			camListPanels.Clear();

			// clear cams
			foreach (SensorCam cam in Program.Cameras) {
				cam.Dispose();
			}
			Program.Cameras.Clear();

			Program.Mergers.Clear();
			Program.Mergers.Add(Program.Merger);

			// clear map
			if (realMap != null) {
				realMap.Dispose();
				realMap = null;
			}
			if (wallMap != null) {
				wallMap.Dispose();
				wallMap = null;
			}
			if (coverageMap != null) {
				coverageMap.Dispose();
				coverageMap = null;
			}

			FloorPlan.Map = null;

			Program.SetStatus("Please choose a map or load a profile.");
			Text = "Indoor Localization";
		}
		/// <summary>
		/// Saves the current profile to the given path.
		/// </summary>
		/// <param name="path">Path for saving, should end with ".profile".</param>
		private void SaveProfile(string path) {
			toolStripProgressBar.Visible = true;

			ThreadPool.QueueUserWorkItem(delegate {
				// prepare serialization
				List<object> toSave = new List<object>();
				BinaryFormatter serializer = new BinaryFormatter();
				Stream file = File.OpenWrite(@path);
				Bitmap saveableMap = realMap.ToBitmap(realMap.Width, realMap.Height);
				toSave.Add(saveableMap);
				toSave.AddRange(Program.Cameras);

				// serialize
				serializer.Serialize(file, toSave);
				file.Close();

				Log("Profile " + Path.GetFileNameWithoutExtension(@path) + " saved successfully");
				Invoke(new MethodInvoker(delegate {
					Text = Path.GetFileNameWithoutExtension(@path) + " - Indoor Localization";
					toolStripProgressBar.Visible = false;
				}));
			});
		}
		/// <summary>
		/// Loads the profile at the given path, initializes the application
		/// and starts people localization.
		/// </summary>
		/// <param name="path">Path for loading, should end with ".profile".</param>
		private void LoadProfile(string path) {
			ClearProfile();
			// prepare deserialization
			List<object> toLoad = new List<object>();
			BinaryFormatter serializer = new BinaryFormatter();
			Stream file = File.OpenRead(@path);

			//deserialize
			toLoad = (List<object>)serializer.Deserialize(file);
			file.Close();

			Log("Loading profile " + Path.GetFileNameWithoutExtension(@path) + "...");

			// create map
			Bitmap map = (Bitmap)toLoad[0];
			toLoad.RemoveAt(0);
			LoadMap(map);

			toolStripProgressBar.Visible = true;
			Program.SetStatus("Adding Cameras...");

			ThreadPool.QueueUserWorkItem(delegate {
				// determine currently connected cams
				List<Guid> guids = new List<Guid>(CLEyeCameraDevice.CameraCount);
				bool allCamsConnected = true;
				for (int i = 0; i < guids.Capacity; i++) {
					guids.Add(CLEyeCameraDevice.CLEyeGetCameraUUID(i));
				}
				// create cams
				List<SensorCam> newCams = new List<SensorCam>();
				foreach (object cam in toLoad) {
					if (guids.Contains(((SensorCam)cam).GUID)) {
						newCams.Add((SensorCam)cam);
					} else {
						allCamsConnected = false;
					}
				}
				Program.LoadCameras(newCams);
				// add cams to GUI
				foreach (SensorCam cam in Program.Cameras) {
					AddCamListEntry(cam);
					FloorPlan.UpdateCamAtCoverageMap(cam, true);
					cam.NameChanged += new EventHandler(ChangeCamName);
				}

				Invoke(new MethodInvoker(delegate { mergerStateView.Invalidate(); }));

				coverageMap = new Image<Bgr, byte>(FloorPlan.DrawCoverageMap());

				Invoke(new MethodInvoker(delegate { toolStripProgressBar.Visible = false; }));

				// show a warning if not all cams are connected
				if (!allCamsConnected) {
                    Invoke(new MethodInvoker(delegate { saveProfileToolStripMenuItem.Enabled = false; }));
					MessageBox.Show(
						"Not all cameras that are used by this profile are currently connected." + Environment.NewLine
						+ "Those cameras have not been added and you will not be able to save this profile.",
						"Warning",
						MessageBoxButtons.OK,
						MessageBoxIcon.Warning,
						MessageBoxDefaultButton.Button1
					);
					Warn("Some cameras are not connected");
				}

				Log("Profile loaded, " + Program.Cameras.Count + " cameras");
				Invoke(new MethodInvoker(delegate { Text = Path.GetFileNameWithoutExtension(@path) + " - Indoor Localization"; }));

				if (Program.Cameras.Count > 0)
					Program.SetStatus("Running...");
				else
					Program.SetStatus("Please add a camera.");
			});
		}

		/// <summary>
		/// Initializes the network panel.
		/// Should be called when network has been started.
		/// </summary>
		private void InitializeNetworkPanel() {
			ownNetworkID.Text = ""+NetworkConnector.NetworkID;
		}

		/// <summary>
		/// Toggles the coverage view mode.
		/// If active, the coverageMap will be shown, otherwise either wallMap or realMap.
		/// While active, drawWalls and drawRays are illegal states, so they will be disabled
		/// and set to their last value when getting back.
		/// </summary>
		private void ToggleCoverageView() {
			drawCoverage = !drawCoverage;
			coverageViewToolStripMenuItem.Image = drawCoverage ?
				Properties.Resources.map_magnify : Properties.Resources.map_magnify_gray;
			coverageViewToolStripButton.Image = drawCoverage ?
				Properties.Resources.map_magnify : Properties.Resources.map_magnify_gray;

			if (drawCoverage && drawWalls) {
				ToggleDrawWalls();
				covRestoreWalls = true;
			} else if (!drawCoverage && !drawWalls && covRestoreWalls) {
				covRestoreWalls = false;
				ToggleDrawWalls();
			}
			drawWallsToolStripMenuItem.Enabled = !drawCoverage;
			drawWallsToolStripButton.Enabled = !drawCoverage;

			if (drawCoverage && drawRays) {
				ToggleDrawRays();
				covRestoreRays = true;
			} else if (!drawCoverage && !drawRays && covRestoreRays) {
				covRestoreRays = false;
				ToggleDrawRays();
			}
			drawRaysToolStripMenuItem.Enabled = !drawCoverage;
			drawRaysToolStripButton.Enabled = !drawCoverage;
		}
		/// <summary>
		/// Toggles the mode that decides if only walls should be displayed as map background.
		/// </summary>
		private void ToggleDrawWalls() {
			drawWalls = !drawWalls;
			drawWallsToolStripMenuItem.Checked = drawWalls;
			drawWallsToolStripButton.Image = drawWalls ?
				Properties.Resources.bullet_black : Properties.Resources.bullet_black_inactive;
		}
		/// <summary>
		/// Toggles the mode that decides if cams should be drawn.
		/// </summary>
		private void ToggleDrawCams() {
			drawCams = !drawCams;
			drawCamerasToolStripMenuItem.Checked = drawCams;
			drawCamerasToolStripButton.Image = drawCams ?
				Properties.Resources.bullet_blue : Properties.Resources.bullet_blue_inactive;
		}
		/// <summary>
		/// Toggles the mode that decides if rays should be drawn.
		/// </summary>
		private void ToggleDrawRays() {
			drawRays = !drawRays;
			drawRaysToolStripMenuItem.Checked = drawRays;
			drawRaysToolStripButton.Image = drawRays ?
				Properties.Resources.bullet_orange : Properties.Resources.bullet_orange_inactive;
		}

        /// <summary>
        /// Toggles the mode that decides if AOI should be drawn.
        /// </summary>
        private void ToggleDrawAOI()
        {
            drawAOI = !drawAOI;
            drawAOIToolStripMenuItem.Checked = drawAOI;           
        }

		/// <summary>
		/// Toggles the mode that decides if entities should be drawn.
		/// </summary>
		private void ToggleDrawEntities() {
			drawEntities = !drawEntities;
			drawEntitiesToolStripMenuItem.Checked = drawEntities;
			drawEntitiesToolStripButton.Image = drawEntities ?
				Properties.Resources.bullet_green : Properties.Resources.bullet_green_inactive;
		}
		/// <summary>
		/// Toggles the mode that decides if the ID of an entity should be drawn.
		/// </summary>
		private void ToggleShowID() {
			showID = !showID;
			showIDToolStripMenuItem.Checked = showID;
		}
		/// <summary>
		/// Toggles the mode that decides if the reliability of an entity should be drawn.
		/// </summary>
		private void ToggleShowReliability() {
			showReliability = !showReliability;
			showReliabilityToolStripMenuItem.Checked = showReliability;
		}
		/// <summary>
		/// Toggles the mode that decides if the sensors that detected an entity should be drawn.
		/// </summary>
		private void ToggleShowOrigin() {
			showOrigin = !showOrigin;
			showOriginsToolStripMenuItem.Checked = showOrigin;
		}
		/// <summary>
		/// Toggles the mode that decides if the exact position of an entity should be drawn.
		/// </summary>
		private void ToggleShowPosition() {
			showPosition = !showPosition;
			showPositionToolStripMenuItem.Checked = showPosition;
		}

		/// <summary>
		/// Switches the view which means that the image (map or cam) that was drawn into
		/// the big image box will be drawn into the small one and vice-versa.
		/// </summary>
		private void SwitchView() {
			ImageBox temp = mapBox;
			mapBox = camBox;
			camBox = temp;
		}

		/// <summary>
		/// Used for the EventHandler of a SensorCam.
		/// Is called every time the name of a cam changed.
		/// </summary>
		/// <param name="sender">The cam that changed its name.</param>
		/// <param name="e">Not used.</param>
		private void ChangeCamName(object sender, EventArgs e) {
			String name = ((SensorCam)sender).Name;
			String formerName = "";
			foreach (Panel camPanel in camListPanels) {
				if (camPanel.Tag.Equals(sender)) {
					Label label = camPanel.Controls.OfType<Label>().ElementAt(0);
					formerName = label.Text;
					label.Text = name;
				}
			}
		}

		/// <summary>
		/// Initializes the panel that is used for adding a new cam.
		/// </summary>
		private void InitializeAddCamPanel() {
			addCamDeviceSelect.Items.Clear();
			// for loop instead of foreach to assure that indices stay the same
			for (int i = 0; i < Program.Devices.Count; i++) {
				addCamDeviceSelect.Items.Add(Program.Devices[i].GUID);
			}
			addCamX.Maximum = FloorPlan.MaxX;
			addCamY.Maximum = FloorPlan.MaxY;
			if (addCamX.Value == 0 && addCamY.Value == 0) {
				addCamX.Value = addCamX.Maximum / 2;
				addCamY.Value = addCamY.Maximum / 2;
			}

			if (addCamDeviceSelect.Items.Count == 0) {
				// no cam connected
				addCamDeviceSelect.Visible = false;
				addCamAdd.Enabled = false;
				addCamSelectLabel.Text = "No additional cameras found.";
				addCamSelectLabel.ForeColor = Color.Firebrick;
				addCamName.Text = "";
			} else {
				// found a cam that can be added
				addCamDeviceSelect.Visible = true;
				addCamAdd.Enabled = true;
				addCamSelectLabel.Text = "Device:";
				addCamSelectLabel.ForeColor = Color.Black;
				addCamDeviceSelect.SelectedIndex = 0;
				addCamName.Text = "Camera " + Program.Cameras.Count;
			}
			addCamName.Select();
		}

		/// <summary>
		/// Adds a new SensorCam to the application
		/// </summary>
		private void AddNewCam() {
			SensorCam newCam = new SensorCam(
				Convert.ToInt32(addCamX.Value),				// x position
				Convert.ToInt32(addCamY.Value),				// y position
				Convert.ToInt32(addCamOrientation.Value),	// orientation
				Convert.ToDouble(addCamPitch.Value),		// pitch
				addCamName.Text,							// name
				(Guid)addCamDeviceSelect.SelectedItem,		// device guid
				Program.Merger);							// merger

			// Add cam to lists (logically and for the GUI)
			Program.Cameras.Add(newCam);
			AddCamListEntry(newCam);

			Program.SetStatus("Updating map...");
			toolStripProgressBar.Visible = true;
			
			// Update coverage map
			ThreadPool.QueueUserWorkItem(delegate {
				FloorPlan.UpdateCamAtCoverageMap(newCam, true);
				coverageMap = new Image<Bgr, byte>(FloorPlan.DrawCoverageMap());
				Invoke(new MethodInvoker(delegate { toolStripProgressBar.Visible = false; }));
				Program.SetStatus("Running...");
			});

			// Add listener for changing name
			newCam.NameChanged += new EventHandler(ChangeCamName);

			Log("Added new camera: "+newCam.Name);
			if (savePath.Equals("")) Text = "Indoor Localization";
			else Text = Path.GetFileNameWithoutExtension(@savePath) + "* - Indoor Localization";
		}

		/// <summary>
		/// Adds a new cam to the GUI by creating a new panel in the Cams-Tab.
		/// The cam will be stored in the tag of the panel.
		/// </summary>
		/// <param name="cam">The cam that should be added to the GUI.</param>
		private void AddCamListEntry(SensorCam cam) {
			Invoke(new MethodInvoker(delegate {
                if (Program.Cameras.Count >= 3)
                    optimizePlacementToolStripMenuItem.Enabled = true;

				if (Program.Cameras.Count > 0)
					motionDebuggingToolStripMenuItem.Enabled = true;

				Panel newEntryPanel = new Panel();
				newEntryPanel.Tag = cam;
				int nextY = (camListPanels.Count > 0) ?
					camListPanels[camListPanels.Count - 1].Location.Y + 26
					: 6;
				newEntryPanel.Location = new Point(6, nextY);
				newEntryPanel.Size = new Size(274, 25);

				CheckBox camActiveCB = new CheckBox();
				camActiveCB.Checked = !cam.IsPaused();
				camActiveCB.Tag = cam;
				camActiveCB.Location = new Point(0, 3);
				camActiveCB.Size = new Size(15, 14);
				camActiveCB.CheckedChanged += PauseResumeCam;
				cam.PausedStateChanged += new EventHandler((object sender, EventArgs e) => {
					camActiveCB.BeginInvoke(new MethodInvoker(() => {
						camActiveCB.Checked = !cam.IsPaused();
					}));
				});

				Label camNameLabel = new Label();
				camNameLabel.Text = cam.Name;
				camNameLabel.Cursor = Cursors.Hand;
				camNameLabel.Tag = cam;
				camNameLabel.Location = new Point(16, 4);
				camNameLabel.Click += SelectCam;

				Button camViewButton = new Button();
				camViewButton.Text = "View";
				camViewButton.Tag = cam;
				camViewButton.Location = new Point(168, 0);
				camViewButton.Click += delegate {
					CamView camView = new CamView(cam);
					camView.Show();
				};

				Button camRemoveButton = new Button();
				camRemoveButton.Text = "X";
				camRemoveButton.ForeColor = Color.Firebrick;
				camRemoveButton.Tag = cam;
				camRemoveButton.Location = new Point(246, 0);
				camRemoveButton.Size = new Size(22, 23);
				camRemoveButton.Click += RemoveCam;

				newEntryPanel.Controls.Add(camActiveCB);
				newEntryPanel.Controls.Add(camNameLabel);
				newEntryPanel.Controls.Add(camViewButton);
				newEntryPanel.Controls.Add(camRemoveButton);

				camScrollPanel.Controls.Add(newEntryPanel);

				camListPanels.Add(newEntryPanel);
			}));
		}

		/// <summary>
		/// Removes a cam from the application and the GUI.
		/// </summary>
		/// <param name="sender">Button in the panel of the cam that should be removed.</param>
		/// <param name="e">Not used.</param>
		private void RemoveCam(object sender, EventArgs e) {
            // Delete selection if selected
			if (selectedCamPanel != null && selectedCamPanel.Equals(((Control)sender).Parent)) {
				selectedCamPanel = null;
				cameraToolStripMenuItem.Enabled = false;
			}

			// Remove at coverage map
			Program.SetStatus("Updating map...");
			toolStripProgressBar.Visible = true;

			ThreadPool.QueueUserWorkItem(delegate {
				FloorPlan.UpdateCamAtCoverageMap((SensorCam)((Control)sender).Tag, false);
				coverageMap = new Image<Bgr, byte>(FloorPlan.DrawCoverageMap());
				Invoke(new MethodInvoker(delegate { toolStripProgressBar.Visible = false; }));
			});

			// Remove SensorCam object
			String camName = ((SensorCam)((Control)sender).Tag).Name;
			Program.Cameras.Remove(((SensorCam)((Control)sender).Tag));
			((SensorCam)((Control)sender).Tag).Dispose();

			// Delete it from the GUI
			int foundSelf = -1;
			for (int i = 0; i < camListPanels.Count; i++) {
				if (camListPanels[i].Equals(((Control)sender).Parent)) {
					((Control)sender).Parent.Dispose();
					foundSelf = i;
				} else if (foundSelf > -1)
					camListPanels[i].Location = new Point(camListPanels[i].Location.X, camListPanels[i].Location.Y - 26);
			}
			camListPanels.RemoveAt(foundSelf);

			// Write log entry and set status if neccessary
			Program.Log("Removed camera: "+camName);
			if (Program.Cameras.Count == 0)
				Program.SetStatus("Please add a camera.");
			else
				Program.SetStatus("Running...");

            if (Program.Cameras.Count < 3)
                optimizePlacementToolStripMenuItem.Enabled = false;

			if (Program.Cameras.Count == 0)
				motionDebuggingToolStripMenuItem.Enabled = false;

			if (savePath.Equals("")) Text = "Indoor Localization";
			else Text = Path.GetFileNameWithoutExtension(@savePath) + "* - Indoor Localization";
		}

		/// <summary>
		/// Selects a cam as the currently active one which means that it is
		/// highlighted on the map and its image will be shown.
		/// </summary>
		/// <param name="sender">A button in the panel of the cam that should be selected.</param>
		/// <param name="e">Not used.</param>
		private void SelectCam(object sender, EventArgs e) {
			// Deselect current one
			if(selectedCamPanel != null)
				((Label)selectedCamPanel.GetChildAtPoint(new Point(16, 4))).ForeColor = SystemColors.ControlText;
			
			// Select new one
			selectedCamPanel = (Panel)((Control)sender).Parent;
			((Label)selectedCamPanel.GetChildAtPoint(new Point(16, 4))).ForeColor = SystemColors.Highlight;

			// Enable menu entry
			cameraToolStripMenuItem.Enabled = true;
		}

		/// <summary>
		/// Pauses a cam if it is running or resumes it if paused.
		/// </summary>
		/// <param name="sender">A button in the panel of the cam.</param>
		/// <param name="e">Not used.</param>
		private void PauseResumeCam(object sender, EventArgs e) {
			if (((CheckBox)sender).Checked)
				((SensorCam)((Control)sender).Tag).Resume();
			else
				((SensorCam)((Control)sender).Tag).Pause();
		}
		//===========================================================
		#endregion

		/// <summary>
		/// Constructs a new MainWindow by initializing all components.
		/// While loading, a splash screen will be displayed.
		/// </summary>
		public MainWindow() {
			bool starting = true;

			// create the splash screen
			Thread splashThread = new Thread(delegate() {
				Application.Run(new SplashScreen());
				while (starting) {
					Thread.Sleep(10);
				}
				Application.ExitThread();
			});
			splashThread.Start();

			// construct window
			InitializeComponent();
			threadCounter.InstanceName = Process.GetCurrentProcess().ProcessName;
			ramCounter.InstanceName = Process.GetCurrentProcess().ProcessName;

			// close splash screen
			starting = false;
			splashThread.Abort();

            //AOI newAOI = new AOI(0, new Rectangle(0, 50, 100, 100));
            //AOI newAOI2 = new AOI(1, new Rectangle(200, 200, 100, 100));
            //myAOIs.addAOI(newAOI);
            //myAOIs.addAOI(newAOI2);
            try
            {
                System.Xml.Serialization.XmlSerializer x = new System.Xml.Serialization.XmlSerializer(myAOIs.GetType());
                FileStream fs = new FileStream("aoi.xml", FileMode.Open);
                myAOIs = (AreasOfInterest)x.Deserialize(fs);
            }
            catch (IOException exc)
            {
                Console.WriteLine(exc.ToString());
            }
		}
	}
}
